import datetime
import uuid
from typing import List, Callable, Dict
import numpy as np


def ResetList(reset_list: List, callback: Callable[..., Dict]) -> List:
    for item in reset_list:
        reset_list = {**item, **callback(item)}
    return reset_list


def ResetKey(reset_dict: Dict, callback: Callable[..., Dict]) -> Dict:
    """ 合并 dict
        Args:
            reset_dict(dict) : 想要修改的数据
            callback(Callable) -> dict : 返回的数据
    """
    return {**reset_dict, **callback(reset_dict)}


def Arr2Str(_list: List, _str: str = ",") -> str:
    return _str.join('%s' % _id for _id in _list)


def GetListKey(_list: List, _key: str = 'id'):
    __list = []
    for item in _list:
        for key in item.keys():
            if key == _key:
                __list.append(item[key])
    return __list


def GetListIndex(_list: List, _index: int = 0):
    assert not _index >= len(_list), '超出范围'
    arr = np.array(_list)
    return arr[:, _index].tolist()


def MatchEnum(_enum, key, none_value=None):
    if key in _enum._member_names_:
        return _enum[key].value
    return none_value


def ResetNoneParam(param, default_value=""):
    return param if param is not None else default_value


def PrintProgress(iteration, total, prefix='', suffix='', decimals=1, barLength=100):
    """ 进度条
        Args:
            iteration (int): 当前进度
            total (int): 总长度
            prefix (str): 显示文字
            suffix (str): 后方显示文字
            decimals (int): 小数点位数
            barLength (int): 进度条总跳数量
    """
    import sys
    format_str = "{0:." + str(decimals) + "f}"
    percent = format_str.format(100 * (iteration / total))
    filled_length = int(round(barLength * iteration / float(total)))
    bar = '#' * filled_length + '-' * (barLength - filled_length)
    sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percent, '%', suffix)),
    if iteration == total:
        sys.stdout.write('\n')
    sys.stdout.flush()


def GeneratorToken():
    import uuid
    return ''.join(str(uuid.uuid4()).split('-')) + ''.join(str(uuid.uuid4()).split('-'))


def RandomStr(length=5, postpix="_"):
    import random
    import string
    return postpix + ''.join(random.sample(string.ascii_letters + string.digits, length))


def RandomChooseStr(length=5, alphabet='abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
    import random
    return "".join(random.sample(alphabet, length))


def Str2Time(time_str):
    import time
    _str_time = "{} {}".format(time_str.date(), time_str.time()).split(".")[0]
    _str_time = time.strptime(_str_time, "%Y-%m-%d %H:%M:%S")
    _str_time = int(time.mktime(_str_time))
    return _str_time


def Time2Str(time_int: int, _format="%Y-%m-%d %H:%M:%S") -> str:
    dateArray = datetime.datetime.fromtimestamp(time_int)
    otherStyleTime = dateArray.strftime(_format)
    return otherStyleTime


def GeneratorOrderCode():
    import time
    order_no = str(time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))) + str(time.time()).replace('.', '')[-7:]
    return order_no


def List2Json(lst):
    import json
    import numpy as np
    keys = [str(x) for x in np.arange(len(lst))]
    list_json = dict(zip(keys, lst))
    str_json = json.dumps(list_json, indent=2, ensure_ascii=False)
    return str_json


def Password(pwd: str, post_fixed: str = "####", key: str = ""):
    import hashlib
    pwd += key
    md5 = hashlib.md5()
    md5.update(str(md5.update(pwd.encode("utf-8"))).encode("utf-8"))
    return post_fixed + md5.hexdigest()


def NowTime(_format="%Y-%m-%d %H:%M:%S.%f"):
    # import time
    # return time.strftime(_format, time.localtime())
    return datetime.datetime.now().strftime(_format)


def IsIdle():
    from ctypes import windll
    out = windll.kernel32.GetStdHandle(-0xb)
    rtn = windll.kernel32.SetConsoleTextAttribute(out, 0x7)
    return rtn


# -------------------------------------------
# -------------------------------------------
# 字体色     |       背景色     |      颜色描述
# -------------------------------------------
#  30      |        40       |       黑色|白色
#  31      |        41       |       红色
#  32      |        42       |       绿色
#  33      |        43       |       黃色
#  34      |        44       |       蓝色
#  35      |        45       |       紫红色
#  36      |        46       |       青蓝色
#  37      |        47       |       白色/灰色
# -------------------------------------------
# -------------------------------
# 显示方式     |      效果
# -------------------------------
#   0        |     终端默认设置
#   1        |     高亮显示
#   22       |     非粗体
#   4        |     下划线
#   24       |     非下划线
#   5        |     闪烁
#   25       |     非闪烁
#   7        |     反显
#   27       |     非反显
# -------------------------------

def Console(valus, console_type="log", start="", end="\n", color="", show_color=True):
    if console_type == "error":
        # star = "*****"
        _type = "ERR "
        color = color if color != "" else 31
    elif console_type == "warn":
        # star = "**** "
        _type = "WARN"
        color = color if color != "" else 33
    elif console_type == "info":
        # star = "***  "
        _type = "INFO"
        color = color if color != "" else 34
    elif console_type == "debug":
        # star = "**   "
        _type = "DEBUG"
        color = color if color != "" else 32
    else:
        # star = "*    "
        start = start if start != "" else "\n"
        _type = "LOG "
        color = color if color != "" else 0

    start_line = ""
    end_line = ""
    if not IsIdle():
        start_line = '\033[1;' + str(color) + 'm'
        end_line = '\033[0m'
    if show_color:
        print(f"{start_line}{start}{_type}:     {NowTime('%Y-%m-%d %H:%M:%S')}     ||     {valus}{end_line}", end=end)
    else:
        print(f"{start}{_type}:     {NowTime('%Y-%m-%d %H:%M:%S')}     ||     {valus}", end=end)


def GetIniFile(ini_path):
    import configparser
    config = configparser.ConfigParser()
    config.read(ini_path)
    return config


def GeneratorIntList(_min: int, _max: int):
    import numpy
    return numpy.array([i for i in range(_min, _max + 1)], dtype="int").tolist()


def LoadJsonFile(json_path) -> dict:
    import json
    import os
    if os.path.exists(json_path):
        with open(json_path, 'r') as load_f:
            load_dict = json.load(load_f)
            return load_dict
    else:
        Console(f"文件不存在: {json_path}", "error")


def AsyncRaise(tid, _exctype):
    import ctypes
    tid = ctypes.c_long(tid)
    import inspect
    if not inspect.isclass(_exctype):
        _exctype = type(_exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(_exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def IsNumber(s):
    try:
        float(s)
        return True
    except ValueError:
        pass
    try:
        import unicodedata
        unicodedata.numeric(s)
        return True
    except (TypeError, ValueError):
        pass

    return False


def LenStr(_str: str, length=20):
    __str = ""
    i = 0
    if len(_str) <= length:
        while i < length - len(_str):
            __str += " "
            i += 1
    return _str + __str


def MakeFile(path):
    import os
    if not os.path.exists(path):
        os.makedirs(path)


def DownloadFile(url, path, file_name="", exists_download=True, complete_callable: Callable = None,
                 progress_callable: Callable = None):
    import time
    import requests
    import os
    MakeFile(path)
    if file_name != "":
        if file_name == "random":
            file_path = path + "/" + str(uuid.uuid4().hex) + "." + str(str(url).split("/")[-1:][0]).split(".")[-1:][0]
        else:
            file_path = path + "/" + file_name
    else:
        file_path = path + "/" + str(url).split("/")[-1:][0]
    start = time.time()
    size = 0
    res = requests.get(url, stream=True)
    chunk_size = 1024
    content_size = int(res.headers["content-length"])

    if os.path.exists(file_path) and not exists_download:
        return file_path
    try:
        if res.status_code == 200:
            file_size = content_size / chunk_size / 1024
            _time = 0
            with open(file_path, 'wb') as f:
                for data in res.iter_content(chunk_size=chunk_size):
                    f.write(data)
                    size += len(data) / chunk_size / 1024
                    end = time.time()
                    _time = end - start
                    if progress_callable:
                        progress_callable(size, file_size, _time)
            if complete_callable:
                complete_callable(file_path, file_size, _time)
            return file_path
    except Exception as e:
        print("下载出错：", e)
        return False


def UnZipFile(file_path, unzip_path_file):
    import zipfile
    with zipfile.ZipFile(file_path, 'r') as file:
        file.extractall(unzip_path_file)
